1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.base.Preconditions.checkArgument;
20  import static com.google.common.base.Preconditions.checkElementIndex;
21  import static com.google.common.base.Preconditions.checkNotNull;
22  
23  import com.google.common.annotations.Beta;
24  import com.google.common.annotations.GwtCompatible;
25  import com.google.common.base.Objects;
26  
27  import java.io.Serializable;
28  import java.util.Arrays;
29  import java.util.Collection;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  
35  import javax.annotation.Nullable;
36  
37  /**
38   * Fixed-size {@link Table} implementation backed by a two-dimensional array.
39   *
40   * <p>The allowed row and column keys must be supplied when the table is
41   * created. The table always contains a mapping for every row key / column pair.
42   * The value corresponding to a given row and column is null unless another
43   * value is provided.
44   *
45   * <p>The table's size is constant: the product of the number of supplied row
46   * keys and the number of supplied column keys. The {@code remove} and {@code
47   * clear} methods are not supported by the table or its views. The {@link
48   * #erase} and {@link #eraseAll} methods may be used instead.
49   *
50   * <p>The ordering of the row and column keys provided when the table is
51   * constructed determines the iteration ordering across rows and columns in the
52   * table's views. None of the view iterators support {@link Iterator#remove}.
53   * If the table is modified after an iterator is created, the iterator remains
54   * valid.
55   *
56   * <p>This class requires less memory than the {@link HashBasedTable} and {@link
57   * TreeBasedTable} implementations, except when the table is sparse.
58   *
59   * <p>Null row keys or column keys are not permitted.
60   *
61   * <p>This class provides methods involving the underlying array structure,
62   * where the array indices correspond to the position of a row or column in the
63   * lists of allowed keys and values. See the {@link #at}, {@link #set}, {@link
64   * #toArray}, {@link #rowKeyList}, and {@link #columnKeyList} methods for more
65   * details.
66   *
67   * <p>Note that this implementation is not synchronized. If multiple threads
68   * access the same cell of an {@code ArrayTable} concurrently and one of the
69   * threads modifies its value, there is no guarantee that the new value will be
70   * fully visible to the other threads. To guarantee that modifications are
71   * visible, synchronize access to the table. Unlike other {@code Table}
72   * implementations, synchronization is unnecessary between a thread that writes
73   * to one cell and a thread that reads from another.
74   *
75   * <p>See the Guava User Guide article on <a href=
76   * "http://code.google.com/p/guava-libraries/wiki/NewCollectionTypesExplained#Table">
77   * {@code Table}</a>.
78   *
79   * @author Jared Levy
80   * @since 10.0
81   */
82  @Beta
83  @GwtCompatible(emulated = true)
84  public final class ArrayTable<R, C, V> extends AbstractTable<R, C, V> implements Serializable {
85  
86    /**
87     * Creates an empty {@code ArrayTable}.
88     *
89     * @param rowKeys row keys that may be stored in the generated table
90     * @param columnKeys column keys that may be stored in the generated table
91     * @throws NullPointerException if any of the provided keys is null
92     * @throws IllegalArgumentException if {@code rowKeys} or {@code columnKeys}
93     *     contains duplicates or is empty
94     */
95    public static <R, C, V> ArrayTable<R, C, V> create(
96        Iterable<? extends R> rowKeys, Iterable<? extends C> columnKeys) {
97      return new ArrayTable<R, C, V>(rowKeys, columnKeys);
98    }
99  
100   /*
101    * TODO(jlevy): Add factory methods taking an Enum class, instead of an
102    * iterable, to specify the allowed row keys and/or column keys. Note that
103    * custom serialization logic is needed to support different enum sizes during
104    * serialization and deserialization.
105    */
106 
107   /**
108    * Creates an {@code ArrayTable} with the mappings in the provided table.
109    *
110    * <p>If {@code table} includes a mapping with row key {@code r} and a
111    * separate mapping with column key {@code c}, the returned table contains a
112    * mapping with row key {@code r} and column key {@code c}. If that row key /
113    * column key pair in not in {@code table}, the pair maps to {@code null} in
114    * the generated table.
115    *
116    * <p>The returned table allows subsequent {@code put} calls with the row keys
117    * in {@code table.rowKeySet()} and the column keys in {@code
118    * table.columnKeySet()}. Calling {@link #put} with other keys leads to an
119    * {@code IllegalArgumentException}.
120    *
121    * <p>The ordering of {@code table.rowKeySet()} and {@code
122    * table.columnKeySet()} determines the row and column iteration ordering of
123    * the returned table.
124    *
125    * @throws NullPointerException if {@code table} has a null key
126    * @throws IllegalArgumentException if the provided table is empty
127    */
128   public static <R, C, V> ArrayTable<R, C, V> create(Table<R, C, V> table) {
129     return (table instanceof ArrayTable<?, ?, ?>)
130       ? new ArrayTable<R, C, V>((ArrayTable<R, C, V>) table)
131       : new ArrayTable<R, C, V>(table);
132   }
133 
134   private final ImmutableList<R> rowList;
135   private final ImmutableList<C> columnList;
136 
137   // TODO(jlevy): Add getters returning rowKeyToIndex and columnKeyToIndex?
138   private final ImmutableMap<R, Integer> rowKeyToIndex;
139   private final ImmutableMap<C, Integer> columnKeyToIndex;
140   private final V[][] array;
141 
142   private ArrayTable(Iterable<? extends R> rowKeys,
143       Iterable<? extends C> columnKeys) {
144     this.rowList = ImmutableList.copyOf(rowKeys);
145     this.columnList = ImmutableList.copyOf(columnKeys);
146     checkArgument(!rowList.isEmpty());
147     checkArgument(!columnList.isEmpty());
148 
149     /*
150      * TODO(jlevy): Support empty rowKeys or columnKeys? If we do, when
151      * columnKeys is empty but rowKeys isn't, the table is empty but
152      * containsRow() can return true and rowKeySet() isn't empty.
153      */
154     rowKeyToIndex = index(rowList);
155     columnKeyToIndex = index(columnList);
156 
157     @SuppressWarnings("unchecked")
158     V[][] tmpArray
159         = (V[][]) new Object[rowList.size()][columnList.size()];
160     array = tmpArray;
161     // Necessary because in GWT the arrays are initialized with "undefined" instead of null.
162     eraseAll();
163   }
164 
165   private static <E> ImmutableMap<E, Integer> index(List<E> list) {
166     ImmutableMap.Builder<E, Integer> columnBuilder = ImmutableMap.builder();
167     for (int i = 0; i < list.size(); i++) {
168       columnBuilder.put(list.get(i), i);
169     }
170     return columnBuilder.build();
171   }
172 
173   private ArrayTable(Table<R, C, V> table) {
174     this(table.rowKeySet(), table.columnKeySet());
175     putAll(table);
176   }
177 
178   private ArrayTable(ArrayTable<R, C, V> table) {
179     rowList = table.rowList;
180     columnList = table.columnList;
181     rowKeyToIndex = table.rowKeyToIndex;
182     columnKeyToIndex = table.columnKeyToIndex;
183     @SuppressWarnings("unchecked")
184     V[][] copy = (V[][]) new Object[rowList.size()][columnList.size()];
185     array = copy;
186     // Necessary because in GWT the arrays are initialized with "undefined" instead of null.
187     eraseAll();
188     for (int i = 0; i < rowList.size(); i++) {
189       System.arraycopy(table.array[i], 0, copy[i], 0, table.array[i].length);
190     }
191   }
192 
193   private abstract static class ArrayMap<K, V> extends Maps.ImprovedAbstractMap<K, V> {
194     private final ImmutableMap<K, Integer> keyIndex;
195 
196     private ArrayMap(ImmutableMap<K, Integer> keyIndex) {
197       this.keyIndex = keyIndex;
198     }
199 
200     @Override
201     public Set<K> keySet() {
202       return keyIndex.keySet();
203     }
204 
205     K getKey(int index) {
206       return keyIndex.keySet().asList().get(index);
207     }
208 
209     abstract String getKeyRole();
210 
211     @Nullable abstract V getValue(int index);
212 
213     @Nullable abstract V setValue(int index, V newValue);
214 
215     @Override
216     public int size() {
217       return keyIndex.size();
218     }
219 
220     @Override
221     public boolean isEmpty() {
222       return keyIndex.isEmpty();
223     }
224 
225     @Override
226     protected Set<Entry<K, V>> createEntrySet() {
227       return new Maps.EntrySet<K, V>() {
228         @Override
229         Map<K, V> map() {
230           return ArrayMap.this;
231         }
232 
233         @Override
234         public Iterator<Entry<K, V>> iterator() {
235           return new AbstractIndexedListIterator<Entry<K, V>>(size()) {
236             @Override
237             protected Entry<K, V> get(final int index) {
238               return new AbstractMapEntry<K, V>() {
239                 @Override
240                 public K getKey() {
241                   return ArrayMap.this.getKey(index);
242                 }
243 
244                 @Override
245                 public V getValue() {
246                   return ArrayMap.this.getValue(index);
247                 }
248 
249                 @Override
250                 public V setValue(V value) {
251                   return ArrayMap.this.setValue(index, value);
252                 }
253               };
254             }
255           };
256         }
257       };
258     }
259 
260     // TODO(user): consider an optimized values() implementation
261 
262     @Override
263     public boolean containsKey(@Nullable Object key) {
264       return keyIndex.containsKey(key);
265     }
266 
267     @Override
268     public V get(@Nullable Object key) {
269       Integer index = keyIndex.get(key);
270       if (index == null) {
271         return null;
272       } else {
273         return getValue(index);
274       }
275     }
276 
277     @Override
278     public V put(K key, V value) {
279       Integer index = keyIndex.get(key);
280       if (index == null) {
281         throw new IllegalArgumentException(
282             getKeyRole() + " " + key + " not in " + keyIndex.keySet());
283       }
284       return setValue(index, value);
285     }
286 
287     @Override
288     public V remove(Object key) {
289       throw new UnsupportedOperationException();
290     }
291 
292     @Override
293     public void clear() {
294       throw new UnsupportedOperationException();
295     }
296   }
297 
298   /**
299    * Returns, as an immutable list, the row keys provided when the table was
300    * constructed, including those that are mapped to null values only.
301    */
302   public ImmutableList<R> rowKeyList() {
303     return rowList;
304   }
305 
306   /**
307    * Returns, as an immutable list, the column keys provided when the table was
308    * constructed, including those that are mapped to null values only.
309    */
310   public ImmutableList<C> columnKeyList() {
311     return columnList;
312   }
313 
314   /**
315    * Returns the value corresponding to the specified row and column indices.
316    * The same value is returned by {@code
317    * get(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex))}, but
318    * this method runs more quickly.
319    *
320    * @param rowIndex position of the row key in {@link #rowKeyList()}
321    * @param columnIndex position of the row key in {@link #columnKeyList()}
322    * @return the value with the specified row and column
323    * @throws IndexOutOfBoundsException if either index is negative, {@code
324    *     rowIndex} is greater then or equal to the number of allowed row keys,
325    *     or {@code columnIndex} is greater then or equal to the number of
326    *     allowed column keys
327    */
328   public V at(int rowIndex, int columnIndex) {
329     // In GWT array access never throws IndexOutOfBoundsException.
330     checkElementIndex(rowIndex, rowList.size());
331     checkElementIndex(columnIndex, columnList.size());
332     return array[rowIndex][columnIndex];
333   }
334 
335   /**
336    * Associates {@code value} with the specified row and column indices. The
337    * logic {@code
338    * put(rowKeyList().get(rowIndex), columnKeyList().get(columnIndex), value)}
339    * has the same behavior, but this method runs more quickly.
340    *
341    * @param rowIndex position of the row key in {@link #rowKeyList()}
342    * @param columnIndex position of the row key in {@link #columnKeyList()}
343    * @param value value to store in the table
344    * @return the previous value with the specified row and column
345    * @throws IndexOutOfBoundsException if either index is negative, {@code
346    *     rowIndex} is greater then or equal to the number of allowed row keys,
347    *     or {@code columnIndex} is greater then or equal to the number of
348    *     allowed column keys
349    */
350   public V set(int rowIndex, int columnIndex, @Nullable V value) {
351     // In GWT array access never throws IndexOutOfBoundsException.
352     checkElementIndex(rowIndex, rowList.size());
353     checkElementIndex(columnIndex, columnList.size());
354     V oldValue = array[rowIndex][columnIndex];
355     array[rowIndex][columnIndex] = value;
356     return oldValue;
357   }
358 
359   /**
360    * Not supported. Use {@link #eraseAll} instead.
361    *
362    * @throws UnsupportedOperationException always
363    * @deprecated Use {@link #eraseAll}
364    */
365   @Override
366   @Deprecated public void clear() {
367     throw new UnsupportedOperationException();
368   }
369 
370   /**
371    * Associates the value {@code null} with every pair of allowed row and column
372    * keys.
373    */
374   public void eraseAll() {
375     for (V[] row : array) {
376       Arrays.fill(row, null);
377     }
378   }
379 
380   /**
381    * Returns {@code true} if the provided keys are among the keys provided when
382    * the table was constructed.
383    */
384   @Override
385   public boolean contains(@Nullable Object rowKey, @Nullable Object columnKey) {
386     return containsRow(rowKey) && containsColumn(columnKey);
387   }
388 
389   /**
390    * Returns {@code true} if the provided column key is among the column keys
391    * provided when the table was constructed.
392    */
393   @Override
394   public boolean containsColumn(@Nullable Object columnKey) {
395     return columnKeyToIndex.containsKey(columnKey);
396   }
397 
398   /**
399    * Returns {@code true} if the provided row key is among the row keys
400    * provided when the table was constructed.
401    */
402   @Override
403   public boolean containsRow(@Nullable Object rowKey) {
404     return rowKeyToIndex.containsKey(rowKey);
405   }
406 
407   @Override
408   public boolean containsValue(@Nullable Object value) {
409     for (V[] row : array) {
410       for (V element : row) {
411         if (Objects.equal(value, element)) {
412           return true;
413         }
414       }
415     }
416     return false;
417   }
418 
419   @Override
420   public V get(@Nullable Object rowKey, @Nullable Object columnKey) {
421     Integer rowIndex = rowKeyToIndex.get(rowKey);
422     Integer columnIndex = columnKeyToIndex.get(columnKey);
423     return (rowIndex == null || columnIndex == null)
424         ? null : at(rowIndex, columnIndex);
425   }
426 
427   /**
428    * Always returns {@code false}.
429    */
430   @Override
431   public boolean isEmpty() {
432     return false;
433   }
434 
435   /**
436    * {@inheritDoc}
437    *
438    * @throws IllegalArgumentException if {@code rowKey} is not in {@link
439    *     #rowKeySet()} or {@code columnKey} is not in {@link #columnKeySet()}.
440    */
441   @Override
442   public V put(R rowKey, C columnKey, @Nullable V value) {
443     checkNotNull(rowKey);
444     checkNotNull(columnKey);
445     Integer rowIndex = rowKeyToIndex.get(rowKey);
446     checkArgument(rowIndex != null, "Row %s not in %s", rowKey, rowList);
447     Integer columnIndex = columnKeyToIndex.get(columnKey);
448     checkArgument(columnIndex != null,
449         "Column %s not in %s", columnKey, columnList);
450     return set(rowIndex, columnIndex, value);
451   }
452 
453   /*
454    * TODO(jlevy): Consider creating a merge() method, similar to putAll() but
455    * copying non-null values only.
456    */
457 
458   /**
459    * {@inheritDoc}
460    *
461    * <p>If {@code table} is an {@code ArrayTable}, its null values will be
462    * stored in this table, possibly replacing values that were previously
463    * non-null.
464    *
465    * @throws NullPointerException if {@code table} has a null key
466    * @throws IllegalArgumentException if any of the provided table's row keys or
467    *     column keys is not in {@link #rowKeySet()} or {@link #columnKeySet()}
468    */
469   @Override
470   public void putAll(Table<? extends R, ? extends C, ? extends V> table) {
471     super.putAll(table);
472   }
473 
474   /**
475    * Not supported. Use {@link #erase} instead.
476    *
477    * @throws UnsupportedOperationException always
478    * @deprecated Use {@link #erase}
479    */
480   @Override
481   @Deprecated public V remove(Object rowKey, Object columnKey) {
482     throw new UnsupportedOperationException();
483   }
484 
485   /**
486    * Associates the value {@code null} with the specified keys, assuming both
487    * keys are valid. If either key is null or isn't among the keys provided
488    * during construction, this method has no effect.
489    *
490    * <p>This method is equivalent to {@code put(rowKey, columnKey, null)} when
491    * both provided keys are valid.
492    *
493    * @param rowKey row key of mapping to be erased
494    * @param columnKey column key of mapping to be erased
495    * @return the value previously associated with the keys, or {@code null} if
496    *     no mapping existed for the keys
497    */
498   public V erase(@Nullable Object rowKey, @Nullable Object columnKey) {
499     Integer rowIndex = rowKeyToIndex.get(rowKey);
500     Integer columnIndex = columnKeyToIndex.get(columnKey);
501     if (rowIndex == null || columnIndex == null) {
502       return null;
503     }
504     return set(rowIndex, columnIndex, null);
505   }
506 
507   // TODO(jlevy): Add eraseRow and eraseColumn methods?
508 
509   @Override
510   public int size() {
511     return rowList.size() * columnList.size();
512   }
513 
514   /**
515    * Returns an unmodifiable set of all row key / column key / value
516    * triplets. Changes to the table will update the returned set.
517    *
518    * <p>The returned set's iterator traverses the mappings with the first row
519    * key, the mappings with the second row key, and so on.
520    *
521    * <p>The value in the returned cells may change if the table subsequently
522    * changes.
523    *
524    * @return set of table cells consisting of row key / column key / value
525    *     triplets
526    */
527   @Override
528   public Set<Cell<R, C, V>> cellSet() {
529     return super.cellSet();
530   }
531 
532   @Override
533   Iterator<Cell<R, C, V>> cellIterator() {
534     return new AbstractIndexedListIterator<Cell<R, C, V>>(size()) {
535       @Override protected Cell<R, C, V> get(final int index) {
536         return new Tables.AbstractCell<R, C, V>() {
537           final int rowIndex = index / columnList.size();
538           final int columnIndex = index % columnList.size();
539           @Override
540           public R getRowKey() {
541             return rowList.get(rowIndex);
542           }
543           @Override
544           public C getColumnKey() {
545             return columnList.get(columnIndex);
546           }
547           @Override
548           public V getValue() {
549             return at(rowIndex, columnIndex);
550           }
551         };
552       }
553     };
554   }
555 
556   /**
557    * Returns a view of all mappings that have the given column key. If the
558    * column key isn't in {@link #columnKeySet()}, an empty immutable map is
559    * returned.
560    *
561    * <p>Otherwise, for each row key in {@link #rowKeySet()}, the returned map
562    * associates the row key with the corresponding value in the table. Changes
563    * to the returned map will update the underlying table, and vice versa.
564    *
565    * @param columnKey key of column to search for in the table
566    * @return the corresponding map from row keys to values
567    */
568   @Override
569   public Map<R, V> column(C columnKey) {
570     checkNotNull(columnKey);
571     Integer columnIndex = columnKeyToIndex.get(columnKey);
572     return (columnIndex == null)
573         ? ImmutableMap.<R, V>of() : new Column(columnIndex);
574   }
575 
576   private class Column extends ArrayMap<R, V> {
577     final int columnIndex;
578 
579     Column(int columnIndex) {
580       super(rowKeyToIndex);
581       this.columnIndex = columnIndex;
582     }
583 
584     @Override
585     String getKeyRole() {
586       return "Row";
587     }
588 
589     @Override
590     V getValue(int index) {
591       return at(index, columnIndex);
592     }
593 
594     @Override
595     V setValue(int index, V newValue) {
596       return set(index, columnIndex, newValue);
597     }
598   }
599 
600   /**
601    * Returns an immutable set of the valid column keys, including those that
602    * are associated with null values only.
603    *
604    * @return immutable set of column keys
605    */
606   @Override
607   public ImmutableSet<C> columnKeySet() {
608     return columnKeyToIndex.keySet();
609   }
610 
611   private transient ColumnMap columnMap;
612 
613   @Override
614   public Map<C, Map<R, V>> columnMap() {
615     ColumnMap map = columnMap;
616     return (map == null) ? columnMap = new ColumnMap() : map;
617   }
618 
619   private class ColumnMap extends ArrayMap<C, Map<R, V>> {
620     private ColumnMap() {
621       super(columnKeyToIndex);
622     }
623 
624     @Override
625     String getKeyRole() {
626       return "Column";
627     }
628 
629     @Override
630     Map<R, V> getValue(int index) {
631       return new Column(index);
632     }
633 
634     @Override
635     Map<R, V> setValue(int index, Map<R, V> newValue) {
636       throw new UnsupportedOperationException();
637     }
638 
639     @Override
640     public Map<R, V> put(C key, Map<R, V> value) {
641       throw new UnsupportedOperationException();
642     }
643   }
644 
645   /**
646    * Returns a view of all mappings that have the given row key. If the
647    * row key isn't in {@link #rowKeySet()}, an empty immutable map is
648    * returned.
649    *
650    * <p>Otherwise, for each column key in {@link #columnKeySet()}, the returned
651    * map associates the column key with the corresponding value in the
652    * table. Changes to the returned map will update the underlying table, and
653    * vice versa.
654    *
655    * @param rowKey key of row to search for in the table
656    * @return the corresponding map from column keys to values
657    */
658   @Override
659   public Map<C, V> row(R rowKey) {
660     checkNotNull(rowKey);
661     Integer rowIndex = rowKeyToIndex.get(rowKey);
662     return (rowIndex == null) ? ImmutableMap.<C, V>of() : new Row(rowIndex);
663   }
664 
665   private class Row extends ArrayMap<C, V> {
666     final int rowIndex;
667 
668     Row(int rowIndex) {
669       super(columnKeyToIndex);
670       this.rowIndex = rowIndex;
671     }
672 
673     @Override
674     String getKeyRole() {
675       return "Column";
676     }
677 
678     @Override
679     V getValue(int index) {
680       return at(rowIndex, index);
681     }
682 
683     @Override
684     V setValue(int index, V newValue) {
685       return set(rowIndex, index, newValue);
686     }
687   }
688 
689   /**
690    * Returns an immutable set of the valid row keys, including those that are
691    * associated with null values only.
692    *
693    * @return immutable set of row keys
694    */
695   @Override
696   public ImmutableSet<R> rowKeySet() {
697     return rowKeyToIndex.keySet();
698   }
699 
700   private transient RowMap rowMap;
701 
702   @Override
703   public Map<R, Map<C, V>> rowMap() {
704     RowMap map = rowMap;
705     return (map == null) ? rowMap = new RowMap() : map;
706   }
707 
708   private class RowMap extends ArrayMap<R, Map<C, V>> {
709     private RowMap() {
710       super(rowKeyToIndex);
711     }
712 
713     @Override
714     String getKeyRole() {
715       return "Row";
716     }
717 
718     @Override
719     Map<C, V> getValue(int index) {
720       return new Row(index);
721     }
722 
723     @Override
724     Map<C, V> setValue(int index, Map<C, V> newValue) {
725       throw new UnsupportedOperationException();
726     }
727 
728     @Override
729     public Map<C, V> put(R key, Map<C, V> value) {
730       throw new UnsupportedOperationException();
731     }
732   }
733 
734   /**
735    * Returns an unmodifiable collection of all values, which may contain
736    * duplicates. Changes to the table will update the returned collection.
737    *
738    * <p>The returned collection's iterator traverses the values of the first row
739    * key, the values of the second row key, and so on.
740    *
741    * @return collection of values
742    */
743   @Override
744   public Collection<V> values() {
745     return super.values();
746   }
747 
748   private static final long serialVersionUID = 0;
749 }